package com.ruoyi.workflow.controller;

import com.alibaba.fastjson.JSONObject;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.system.domain.vo.SysOssVo;
import com.ruoyi.system.service.ISysOssService;
import com.ruoyi.system.service.ISysUserService;
import com.ruoyi.workflow.common.constant.FaceConstant;
import com.ruoyi.workflow.service.IFaceRecognitionService;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

import java.util.List;

/**
 * @描述 人脸识别相关接口controller类
 * @版本 v1
 * @作者 wangxuqing
 * @日期 2023-07-26 09:20
 */
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/workflow/FaceRecognition")
public class FaceRecognitionController {
    @Autowired
    private ISysUserService userService;
    @Autowired
    private IFaceRecognitionService faceRecognitionService;


    @Autowired
    private ISysOssService iSysOssService;


    /**
     * @描述 创建人脸库
     * @作者 wangxuqing
     * @日期 2023-07-26 09:36
     * @参数 outerId 待创建的人脸库的全局唯一的自定义标识，可使用该名称来查询人脸库信息
     * @返回值 R
     * @异常
     * @修改人和其它信息
     */
    @PostMapping("/faceset/create")
    public R createFaceSet(@RequestParam(value = "outerId", required = false) String outerId) {
        return R.ok(faceRecognitionService.createFaceSet(outerId));
    }

    /**
     * @描述 获取人脸库列表（前1000，实际上创建的人脸库应该也不会超过1000）
     * @作者 wangxuqing
     * @日期 2023-07-27 08:39
     * @返回值 R
     * @异常
     * @修改人和其它信息
     */
    @PostMapping("/faceset/getfacesets")
    public R getAllFaceSets() {
        return R.ok(faceRecognitionService.getAllFaceSets());
    }

    /**
     * @描述 获取人脸库详情
     * @作者 wangxuqing
     * @日期 2023-07-27 09:24
     * @参数 outerId 人脸库的全局唯一的自定义标识（与faceSetToken二选一，优先outerId）
     * @参数 faceSetToken Face++对于人脸库的全局唯一标识（与outerId二选一）
     * @参数 start 表示开始返回的 face_token 在本 FaceSet 中的序号， n 是 [1,10000] 间的一个整数。每次返回 100 个 face_token。默认值为 1。
     * @返回值 R
     * @异常
     * @修改人和其它信息
     */
    @PostMapping("/faceset/detail")
    public R getDetailOfFaceSet(@RequestParam(value = "outerId", required = false) String outerId,
                                @RequestParam(value = "faceSetToken", required = false) String faceSetToken,
                                @RequestParam(value = "start", required = false) Integer start) {
        if (outerId == null && faceSetToken == null) {
            return R.fail("outerId, faceSetToken中必须传一个参数");
        }
        return R.ok(faceRecognitionService.getDetailOfFaceSet(outerId, faceSetToken, start));
    }

    /**
     * @描述 删除人脸库
     * @作者 wangxuqing
     * @日期 2023-07-27 09:15
     * @参数 outerId 人脸库的全局唯一的自定义标识（与faceSetToken二选一，优先outerId）
     * @参数 faceSetToken Face++对于人脸库的全局唯一标识（与outerId二选一）
     * @参数 checkEmpty 删除时是否检查FaceSet中是否存在face_token，默认值为1   0：不检查，1：检查  如果设置为1，当FaceSet中存在face_token则不能删除
     * @返回值 R
     * @异常
     * @修改人和其它信息
     */
    @PostMapping("/faceset/delete")
    public R deleteFaceSets(@RequestParam(value = "outerId", required = false) String outerId,
                            @RequestParam(value = "faceSetToken", required = false) String faceSetToken,
                            @RequestParam(value = "checkEmpty", required = false) Integer checkEmpty) {
        if (outerId == null && faceSetToken == null) {
            return R.fail("outerId, faceSetToken中必须传一个参数");
        }
        return R.ok(faceRecognitionService.deleteFaceSet(outerId, faceSetToken, checkEmpty));
    }

    /**
     * @描述 添加单个人脸
     * @作者 wangxuqing
     * @日期 2023-07-27 10:44
     * @参数 outerId 人脸库的全局唯一的自定义标识（与faceSetToken二选一，优先outerId）
     * @参数 faceSetToken Face++对于人脸库的全局唯一标识（与outerId二选一）
     * @参数 faceToken 人脸的唯一标识
     * @参数 userId 对应sys_user中的user_id，同时作为人脸的自定义标识
     * @返回值 R
     * @异常
     * @修改人和其它信息
     */
    @PostMapping("/faceset/addface")
    public R addFace(@RequestParam(value = "outerId", required = false) String outerId,
                     @RequestParam(value = "faceSetToken", required = false) String faceSetToken,
                     @RequestParam(value = "image") MultipartFile image,
                     @RequestParam(value = "userId") String userId) {
        if (outerId == null && faceSetToken == null) {
            return R.fail("outerId, faceSetToken中必须传一个参数");
        }
        R detectResult = detect(image);
        JSONObject detectResultFromFacePlusPlus = JSONObject.parseObject(JSONObject.toJSONString(detectResult.getData()));
        if (detectResultFromFacePlusPlus == null ||detectResultFromFacePlusPlus.getString("error_message") != null || detectResultFromFacePlusPlus.getInteger("face_num") != 1) {
            return R.fail(detectResultFromFacePlusPlus);
        }
        JSONObject face = JSONObject.parseObject(JSONObject.toJSONString(detectResultFromFacePlusPlus.getJSONArray("faces").get(0)));
        String faceToken = face.getString("face_token");
        if (faceToken == null) {
            return R.fail("face_token不存在");
        }
        SysOssVo upload = iSysOssService.upload(image);
        return R.ok(faceRecognitionService.addFace(outerId, faceSetToken, faceToken, userId, upload != null && upload.getUrl() != null ? String.valueOf(upload.getUrl()) : null));
    }

    /**
     * @描述 批量添加人脸
     * @作者 wangxuqing
     * @日期 2023-07-27 13:39
     * @参数 file 人脸压缩包
     * @返回值 R
     * @异常
     * @修改人和其它信息
     */
    @PostMapping("/faceset/addface/zip")
    public R addFaceByZip(@RequestParam(value = "file") MultipartFile file,
                          @RequestParam(value = "outerId", required = false) String outerId,
                          @RequestParam(value = "faceSetToken", required = false) String faceSetToken) {
        // 对压缩文件进行解压
        if (file.isEmpty()) {
            return R.fail("压缩文件不能为空");
        }
        if (outerId == null && faceSetToken == null) {
            return R.fail("outerId, faceSetToken中必须传一个参数");
        }
        return R.ok(faceRecognitionService.addFaceByZip(file, outerId, faceSetToken));
    }

    /**
     * @描述 根据faceToken删除人脸
     * @作者 wangxuqing
     * @日期 2023-07-28 10:02
     * @参数 outerId 人脸库的全局唯一的自定义标识（与faceSetToken二选一，优先outerId）
     * @参数 faceSetToken Face++对于人脸库的全局唯一标识（与outerId二选一）
     * @参数 faceToken 人脸的唯一标识
     * @返回值 R
     * @异常
     * @修改人和其它信息
     */
    @PostMapping("/face/removebytoken")
    public R removeFaceByFaceToken(@RequestParam(value = "outerId", required = false) String outerId,
                                   @RequestParam(value = "faceSetToken", required = false) String faceSetToken,
                                   @RequestParam(value = "faceToken") String faceToken) {
        if (outerId == null && faceSetToken == null) {
            return R.fail("outerId, faceSetToken中必须传一个参数");
        }
        return R.ok(faceRecognitionService.removeFaceByFaceToken(outerId, faceSetToken, faceToken));
    }

    /**
     * @描述 根据faceToken的集合批量删除人脸
     * @作者 wangxuqing
     * @日期 2023-07-28 10:04
     * @参数 outerId 人脸库的全局唯一的自定义标识（与faceSetToken二选一，优先outerId）
     * @参数 faceSetToken Face++对于人脸库的全局唯一标识（与outerId二选一）
     * @参数 faceTokenList faceToken的集合
     * @返回值 R
     * @异常
     * @修改人和其它信息
     */
    @PostMapping("/face/remove/batch")
    public R removeFaceByFaceTokenList(@RequestParam(value = "outerId", required = false) String outerId,
                                       @RequestParam(value = "faceSetToken", required = false) String faceSetToken,
                                       @RequestParam(value = "faceTokenList") List<String> faceTokenList) {
        if (outerId == null && faceSetToken == null) {
            return R.fail("outerId, faceSetToken中必须传一个参数");
        }
        return R.ok(faceRecognitionService.removeFaceByFaceTokenList(outerId, faceSetToken, faceTokenList));
    }

    /**
     * @描述 根据userId删除人脸
     * @作者 wangxuqing
     * @日期 2023-07-28 10:09
     * @参数 outerId 人脸库的全局唯一的自定义标识（与faceSetToken二选一，优先outerId）
     * @参数 faceSetToken Face++对于人脸库的全局唯一标识（与outerId二选一）
     * @参数 userId 用户Id
     * @返回值 R
     * @异常
     * @修改人和其它信息
     */
    @PostMapping("/face/removebyuserid")
    public R removeFaceByUserId(@RequestParam(value = "outerId", required = false) String outerId,
                                @RequestParam(value = "faceSetToken", required = false) String faceSetToken,
                                @RequestParam(value = "userId") Long userId) {
        if (outerId == null && faceSetToken == null) {
            return R.fail("outerId, faceSetToken中必须传一个参数");
        }
        return R.ok(faceRecognitionService.removeFaceByUserId(outerId, faceSetToken, userId));
    }

    /**
     * @描述 人脸检测，需要face_token的接口可以先调用该接口获取face_token
     * @作者 wangxuqing
     * @日期 2023-07-27 08:40
     * @参数 image 待检测的人脸图片
     * @返回值 R
     * @异常
     * @修改人和其它信息
     */
    @PostMapping("/face/detect")
    public R detect(@RequestParam(value = "image") MultipartFile image) {
        // 对图片进行校验
        if (!isImageFormatValid(image)) {
            return R.fail("图片格式不正确，必须为jpg、jpeg或png格式！");
        }

        // 对图片进行处理，使其满足Face++接口的要求
        String fileExtension = image.getOriginalFilename().substring(image.getOriginalFilename().lastIndexOf(".") + 1);
        byte[] imageBytes = faceRecognitionService.processImage(image, FaceConstant.MIN_IMAGE_WIDTH, FaceConstant.MIN_IMAGE_HEIGHT,
            FaceConstant.MAX_IMAGE_WIDTH, FaceConstant.MAX_IMAGE_HEIGHT, FaceConstant.MAX_IMAGE_SIZE,
            fileExtension);
        return R.ok(faceRecognitionService.detect(imageBytes));
    }

    /**
     * @描述 人脸搜索
     * @作者 wangxuqing
     * @日期 2023-07-28 08:55
     * @参数 image 待搜索的人脸图片
     * @参数 outerId 人脸库的全局唯一的自定义标识（与faceSetToken二选一，优先outerId）
     * @参数 faceSetToken Face++对于人脸库的全局唯一标识（与outerId二选一）
     * @返回值 R
     * @异常
     * @修改人和其它信息
     */
    @PostMapping("/face/search")
    public R search(@RequestParam(value = "image") MultipartFile image,
                    @RequestParam(value = "outerId", required = false) String outerId,
                    @RequestParam(value = "faceSetToken", required = false) String faceSetToken,
                    @RequestParam(required = false, value = "userId") String userId
                    ) {
        // 对图片进行校验
        if (!isImageFormatValid(image)) {
            return R.fail("图片格式不正确，必须为jpg、jpeg或png格式！");
        }

        if (outerId == null && faceSetToken == null) {
            return R.fail("outerId, faceSetToken中必须传一个参数");
        }

        // 对图片进行处理，使其满足Face++接口的要求
        String fileExtension = image.getOriginalFilename().substring(image.getOriginalFilename().lastIndexOf(".") + 1);
        byte[] imageBytes = faceRecognitionService.processImage(image, FaceConstant.MIN_IMAGE_WIDTH, FaceConstant.MIN_IMAGE_HEIGHT,
            FaceConstant.MAX_IMAGE_WIDTH, FaceConstant.MAX_IMAGE_HEIGHT, FaceConstant.MAX_IMAGE_SIZE,
            fileExtension);
        JSONObject searchResult = faceRecognitionService.search(imageBytes, outerId, faceSetToken);

        // 如果置信值低于“千分之一”阈值则不建议认为是同一个人；如果置信值超过“十万分之一”阈值，则是同一个人的几率非常高。
        // 判断人脸库中是否有该人脸信息
        if (searchResult.containsKey("error_message")) {
            return R.fail(searchResult.getString("error_message"));
        }

        if (!searchResult.containsKey("results")) {
            return R.fail("该照片未检测到人脸，请重新拍照");
        }

        JSONObject thresholds = searchResult.getJSONObject("thresholds"); // 置信度阈值。1e-3：误识率为千分之一的置信度阈值；1e-4：误识率为万分之一的置信度阈值；1e-5：误识率为十万分之一的置信度阈值
        JSONObject result = searchResult.getJSONArray("results").getJSONObject(0); // 人脸搜索的结果
        float confidence = result.getFloat("confidence"); // 比对结果置信度
        float thresholdMax = thresholds.getFloat("1e-5");
        float thresholdMin = thresholds.getFloat("1e-3");
        if (confidence < thresholdMin) {
            return R.fail("最高置信度小于误识率为千分之一的置信度阈值，极大的可能人脸库中未录入该人脸");
        }
        if (confidence < thresholdMax) {
            return R.fail("最高置信度小于误识率为万分之一的置信度阈值，有可能人脸库中未录入该人脸");
        }

        // 获取人脸搜索结果对应的用户信息
        String resultId = result.getString("user_id");
        if (userId.equals(resultId)){
            return R.ok(true);
        }else {
            return R.ok(false);
        }
    }

    private Boolean isImageFormatValid(MultipartFile image) {
        String originalFilename = image.getOriginalFilename();
        if (StringUtils.isEmpty(originalFilename)) {
            return false;
        }
        String fileExtension = originalFilename.substring(originalFilename.lastIndexOf(".") + 1);
        if (StringUtils.isEmpty(fileExtension)) {
            return false;
        }
        return "jpg".equals(fileExtension) || "jpeg".equals(fileExtension) || "png".equals(fileExtension);
    }

}
